#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <functional>
#include <pthread.h>
#include "Sock.hpp"

static const uint16_t defaultport = 8080;

using func_t = std::function<std::string(std::string &)>;

class HttpServer;

struct ThreadData
{
    ThreadData(int s, const std::string &ip, const uint16_t &port, HttpServer *p)
        : _sock(s), _ip(ip), _port(port), tsvrp(p)
    {
    }

    ~ThreadData()
    {
    }

    int _sock;
    std::string _ip;
    uint16_t _port;
    HttpServer *tsvrp;
};

class HttpServer
{
public:
    HttpServer(func_t func, int port = defaultport) : _func(func), _port(port)
    {
    }
    ~HttpServer()
    {
    }

    void InitServer()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listhen();
        //std::cout << "init finish" << std::endl;
    }

    void HeaderHttpRequest(int sock)
    {
        char buffer[4096];
        std::string request;
        ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
        //std::cout << buffer << std::endl;
        if (s > 0)
        {
            buffer[s] = '\0';
            request = buffer;
            std::string response = _func(request);
            send(sock, response.c_str(), response.size(), 0);
        }
        else
        {
            logmessage(INFO, "client quit...");
        }
    }

    static void *ThreadRoutine(void *args)
    {
        //std::cout << "thread is running" << std::endl;
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);

        td->tsvrp->HeaderHttpRequest(td->_sock);
        close(td->_sock);

        delete td;
        return nullptr;
    }

    void Start()
    {
        //std::cout << "ready" << std::endl;
        while (true)
        {
            std::string clientip;
            uint16_t clientport;
            int sock = _listensock.Accept(&clientip, &clientport);
            if (sock < 0)
                continue;
            //std::cout << "accept success" << std::endl;

            pthread_t pid;
            ThreadData *td = new ThreadData(sock, clientip, clientport, this);
            //std::cout << "data creat" << std::endl;
            pthread_create(&pid, nullptr, ThreadRoutine, td);
        }
    }

private:
    uint16_t _port;
    Sock _listensock;
    func_t _func;
};
